/***************************************************************
*  Copyright (c) 2007 by GroupMe! Team (www.groupme.net)
*  All rights reserved.
*
*  This file is part of the GroupMe! Project. Source code of 
*  this project is closed and redistribution of this code is
*  prohibited. 
*  
*  Contact: http://www.groupme.net
*
*  This copyright notice MUST APPEAR in all copies of the file!
***************************************************************/
package net.groupme.controller;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.groupme.api.API_CONSTANTS;
import net.groupme.exceptions.InsertFailedException;
import net.groupme.exceptions.UserNotExistingException;
import net.groupme.gui.constants.VIEW_VOCABULARY;
import net.groupme.model.ObjectFactory;
import net.groupme.model.OnlineStatus;
import net.groupme.user.UserManager;
import net.groupme.user.UserManagerMessages;
import net.groupme.utils.HTTPUtilities;
import net.groupme.utils.sql.UserQueryUtility;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.runner.Request;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import org.springframework.web.servlet.mvc.SimpleFormController;
import org.springframework.web.util.CookieGenerator;


/**
 * created on 18/06/2007 
 * 
 * This GroupMe! controller handles requests like <i>login</i> or <i>registration</i> of users.
 * 
 * TODO: Normally we should simply extend {@link SimpleFormController}...
 * 
 * @author Fabian Abel, <a href="mailto:abel@l3s.de">abel@l3s.de</a>
 * @author last edited by $Author: fabian $
 * 
 * @version $Revision: 1.17 $ $Date: 2008-11-07 16:24:05 $
 */
public class UserManagementController implements Controller, ApplicationContextAware {
	
	/** Logger for this class and subclasses */
	protected final Log logger = LogFactory.getLog(getClass());
	
	
	/** the application context */
	ApplicationContext applicationContext = null;
	
	/**
	 * Set the ApplicationContext that this object runs in. 
	 * @param applicationContext the current {@link ApplicationContext} 
	 */
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}



	/**
	 * This method handles requests that deal with user management stuff.
	 * 
	 * @param request the HTTP request
	 * @param response the HTTP response
	 * 
	 * @return the Spring {@link ModelAndView} object which includes also object that
	 * can be accessed by the view (e.g. the actual user object...)
	 */
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
    	String userRequestPath = request.getRequestURI();

    	String userRequest = userRequestPath.substring(userRequestPath.lastIndexOf("/") + 1);
    	if (request.getQueryString() != null) {
    		userRequestPath = userRequestPath + "?" + request.getQueryString();
    	}
    	//create the MaV:
    	ModelAndView mav = new ModelAndView();
    	//output:
    	String output = getStringValue(request, API_CONSTANTS.PARAMETER_OUTPUT);
    	if(output == null){//Standard output format is HTML:
    		output = API_CONSTANTS.OUTPUT_HTML;
    	}
    	
    	//show user page?
    	if(request.getRequestURI().contains(VIEW_VOCABULARY.RESTful_USER_URI_PATTERN_PARTIAL)){
    		String username = getStringValue(request, VIEW_VOCABULARY.PARAMETER_USERNAME);
    		logger.info("User that should be shown: " + username);
    		try {
				mav.addObject("user", ObjectFactory.getUser(username));
				if(API_CONSTANTS.OUTPUT_RDF.equals(output) || API_CONSTANTS.OUTPUT_RDF.equals(HTTPUtilities.detectRequestedOutputFormat(request))){
	    			mav.setViewName(API_CONSTANTS.VIEW_RDF_USER);
	    		}else{
	    			mav.setViewName(VIEW_VOCABULARY.VIEW_USER);	
	    		}
				return mav;
			} catch (UserNotExistingException e) {
				e.printStackTrace();
				mav.setViewName(VIEW_VOCABULARY.VIEW_GENERAL_ERROR);
				return mav;
			}
    	}
    	
    	logger.info("Requested Site/Action: " + userRequest);
    	
    	//switch cases that can be handled by this controller:
    	if(VIEW_VOCABULARY.ACTION_REGISTRATION.equals(userRequest)){
    		mav = registerUser(request, response);
    	}else if(VIEW_VOCABULARY.ACTION_LOGIN.equals(userRequest) || VIEW_VOCABULARY.ACTION_HEADER_LOGIN.equals(userRequest)){
    		mav = login(request, response);
    	}else if(VIEW_VOCABULARY.ACTION_LOGOUT.equals(userRequest)){
    		mav = logout(request,response);
    	}

    	logger.info("Indicator of result view: " + mav.getViewName());
    	//add redirect infos
    	if (VIEW_VOCABULARY.ACTION_HEADER_LOGIN.equals(userRequest)){
    		if (VIEW_VOCABULARY.ACTION_HEADER_LOGIN.equals(userRequest)){
    			userRequestPath = request.getParameter(VIEW_VOCABULARY.PARAMETER_REDIRECT);
    		}
    		logger.info("Adding redirect infos: " + userRequestPath);
    		mav.addObject(VIEW_VOCABULARY.PARAMETER_REDIRECT, userRequestPath);
    	}
    	return mav;
    }   
    
    
    
    /**
     * This method handles registration requests.
	 * 
	 * @param request the HTTP request
	 * @param response the HTTP response
	 * 
     * @return the Spring {@link ModelAndView} object which includes the actual user object and 
     * points to a view identifier
     */
    private ModelAndView registerUser(HttpServletRequest request, HttpServletResponse response){
    	//username, password,...:
    	String username = getStringValue(request, VIEW_VOCABULARY.PARAMETER_USERNAME);
    	String password = getStringValue(request, VIEW_VOCABULARY.PARAMETER_PASSWORD);
    	String firstName = getStringValue(request, VIEW_VOCABULARY.PARAMETER_FIRST_NAME);
    	String lastName = getStringValue(request, VIEW_VOCABULARY.PARAMETER_LAST_NAME);
    	String email = getStringValue(request, VIEW_VOCABULARY.PARAMETER_EMAIL);
    	String foaf = getStringValue(request, VIEW_VOCABULARY.PARAMETER_FOAF);
    	String photo = getStringValue(request, VIEW_VOCABULARY.PARAMETER_PHOTO);
    	
    	logger.debug("username: " + username + " - password: " + password + " - mail: " + email + " - firstname: " + firstName + " - lastname: " + lastName + " - foaf: " + foaf);
    	
    	ModelAndView mav = new ModelAndView();    	
    			
		//associate userManager with instantiated user object:
    	UserManager manager = (UserManager) applicationContext.getBean(VIEW_VOCABULARY.USER_MANGER, UserManager.class);
    	UserManagerMessages result = manager.register(username, password, email, firstName, lastName, foaf, photo);
	
    	if(UserManagerMessages.REGISTRATION_SUCCESSFUL.equals(result)){
    		mav.setViewName(VIEW_VOCABULARY.VIEW_SUCCESSFUL_REGISTRATION);
    	}else{
    		//set data for forms...
    		mav.addObject(VIEW_VOCABULARY.PARAMETER_USERNAME, username);
        	mav.addObject(VIEW_VOCABULARY.PARAMETER_PASSWORD, password);
        	mav.addObject(VIEW_VOCABULARY.PARAMETER_FIRST_NAME, firstName);
        	mav.addObject(VIEW_VOCABULARY.PARAMETER_LAST_NAME, lastName);
        	mav.addObject(VIEW_VOCABULARY.PARAMETER_EMAIL, email);
        	mav.addObject(VIEW_VOCABULARY.PARAMETER_FOAF, foaf);
        	mav.addObject(VIEW_VOCABULARY.PARAMETER_FOAF, photo);
        	
    		if(UserManagerMessages.USERNAME_ALLREADY_EXISTS.equals(result)){
    			mav.setViewName(VIEW_VOCABULARY.VIEW_REGISTRATION_FAILED);
    			logger.info("Username already exists!");
    			mav.addObject(VIEW_VOCABULARY.STATUS_REGISTRATION, "Username already exists!");
    		}else if(UserManagerMessages.UPDATE_OR_INSERT_FAILED.equals(result)){
    			mav.setViewName(VIEW_VOCABULARY.VIEW_REGISTRATION_FAILED);
    			logger.info("Problems during insert/update operation!!");
    			mav.addObject(VIEW_VOCABULARY.STATUS_REGISTRATION, "Problems during insert/update operation!!");
    		} 	
    	}
			
    	return mav;
    }
    
    
    /**
     * This method handles login requests.
	 * 
	 * @param request the HTTP request
	 * @param response the HTTP response
	 * 
     * @return the Spring {@link ModelAndView} object which includes the actual user object and 
     * points to a view identifier
     */
    private ModelAndView login(HttpServletRequest request, HttpServletResponse response){
    	//username, password:
    	String username = getStringValue(request, VIEW_VOCABULARY.PARAMETER_USERNAME);
    	String password = getStringValue(request, VIEW_VOCABULARY.PARAMETER_PASSWORD);
    	logger.info("username: " + username + " - password: " + password);
    	
    	ModelAndView mav = new ModelAndView();
    	
    	UserManager manager = (UserManager) applicationContext.getBean(VIEW_VOCABULARY.USER_MANGER, UserManager.class);
    	UserManagerMessages result = manager.login(username, password);
    	if(VIEW_VOCABULARY.ACTION_LOGIN_VIA_BOOKMARKLET.equals(getStringValue(request, VIEW_VOCABULARY.PARAMETER_ACTION))){
			String url = getStringValue(request, VIEW_VOCABULARY.PARAMETER_URL);
			String title = getStringValue(request, VIEW_VOCABULARY.PARAMETER_TITLE);
			mav.addObject(VIEW_VOCABULARY.PARAMETER_URL, url);
			mav.addObject(VIEW_VOCABULARY.PARAMETER_TITLE, title);
    	}
    	logger.info("User: " + username + " logged in.");
    	if(UserManagerMessages.LOGIN_SUCCESSFUL.equals(result)){    		
    		//cookie stuff:
    		String permanentLogin = getStringValue(request, VIEW_VOCABULARY.PARAMETER_KEEP_PERMANENT_LOGGED_IN);
    		String credential = null;
    		if(permanentLogin != null){
    			//user:
    			CookieGenerator cg = new CookieGenerator();
    			cg.setCookieName(VIEW_VOCABULARY.COOKIE_PERMANENT_LOGIN_USER_ID);
    			cg.setCookieMaxAge(VIEW_VOCABULARY.COOKIE_PERMANENT_LOGIN_LIFETIME);
    			cg.addCookie(response, manager.getUser().getId());
    			
    			//credential:
    			credential = manager.getUser().hashCode()+"";
    			cg = new CookieGenerator();
    			cg.setCookieName(VIEW_VOCABULARY.COOKIE_PERMANENT_LOGIN_CREDENTIAL);
    			cg.setCookieMaxAge(VIEW_VOCABULARY.COOKIE_PERMANENT_LOGIN_LIFETIME);
    			cg.addCookie(response, credential);
    		
    			logger.info("Permanent login - Cookies have to be enabled...");
    		}
			//update online status:
    		try {
				UserQueryUtility.updateUserStatus(manager.getUser().getId(), OnlineStatus.ONLINE, credential);
			} catch (InsertFailedException e) {
				logger.info("Updating online status failed!");
				e.printStackTrace();
			}
    		
    		//Via Bookmarklet:
    		if(VIEW_VOCABULARY.ACTION_LOGIN_VIA_BOOKMARKLET.equals(getStringValue(request, VIEW_VOCABULARY.PARAMETER_ACTION))){
    			mav.setViewName(VIEW_VOCABULARY.VIEW_ADD_TO_GROUP_VIA_BOOKMARKLET);
        	}else{
        		mav.setViewName(VIEW_VOCABULARY.VIEW_SUCCESSFUL_LOGGED_IN);
        	}
    		
    	}else {
    		mav.setViewName(VIEW_VOCABULARY.VIEW_LOGIN_FAILED);
    		
    		if(UserManagerMessages.PASSWORD_INCORRECT.equals(result)){
    			mav.addObject(VIEW_VOCABULARY.STATUS_LOGIN, "Password incorrect.");
    			mav.addObject(VIEW_VOCABULARY.PARAMETER_USERNAME, username);
    		}else if(UserManagerMessages.PASSWORD_INCORRECT.equals(result)){
    			mav.addObject(VIEW_VOCABULARY.STATUS_LOGIN, "Username does not exist.");
    		}else{
    			mav.addObject(VIEW_VOCABULARY.STATUS_LOGIN, "Login failed. Please contact us if the problem persists.");
    		}
    		if(VIEW_VOCABULARY.ACTION_LOGIN_VIA_BOOKMARKLET.equals(getStringValue(request, VIEW_VOCABULARY.PARAMETER_ACTION))){
    			mav.setViewName(VIEW_VOCABULARY.VIEW_ADD_TO_GROUP_VIA_BOOKMARKLET);
        	}
    	}
    	//login from admin view?
    	if(request.getRequestURI().contains(VIEW_VOCABULARY.ACTION_ADMIN_CONTEXTPATH)){
    		mav.setViewName(VIEW_VOCABULARY.VIEW_ADMIN);
    	}
    	
		return mav;
    }
    
    /**
     * This method logs out the actual user.
	 * 
	 * 
     * @return the Spring {@link ModelAndView} object which points the user to the 
     * welcome page 
     */
    private ModelAndView logout(HttpServletRequest request, HttpServletResponse response){
    	logger.info("logout user");
    	
    	ModelAndView mav = new ModelAndView();
    	
    	UserManager manager = (UserManager) applicationContext.getBean(VIEW_VOCABULARY.USER_MANGER, UserManager.class);
    	
		//update online status:
		try {
			if(manager.getUser() != null){
				UserQueryUtility.updateUserOnlineStatus(manager.getUser().getId(), OnlineStatus.LOGGED_OUT);
			}
		} catch (InsertFailedException e) {
			logger.info("Updating online status failed!");
			e.printStackTrace();
		}

    	//delete cookies TODO: geht irgendwie nich :( -> jetzt wird es aber nicht mehr benötigt
//    	Cookie credential = WebUtils.getCookie(request, VIEW_VOCABULARY.COOKIE_PERMANENT_LOGIN_CREDENTIAL);
//    	if(passwordCookie != null){
    		
    		/*CookieGenerator cg = new CookieGenerator();
    		cg.setCookieName(VIEW_VOCABULARY.COOKIE_PERMANENT_LOGIN_PASSWORD);
    		cg.setCookiePath("/GroupMe");
    		cg.setCookieMaxAge(-1);
    		cg.removeCookie(response);*/
    		
//    		Cookie pass = new Cookie(VIEW_VOCABULARY.COOKIE_PERMANENT_LOGIN_PASSWORD, "");
//    		pass.setPath("/GroupMe");
//    		pass.setMaxAge(0);
//    		response.addCookie(pass);
//    		
//    		Cookie user = new Cookie(VIEW_VOCABULARY.COOKIE_PERMANENT_LOGIN_USERNAME, "");
//    		user.setPath("/GroupMe");
//    		user.setMaxAge(0);
//    		response.addCookie(user);
    		
//    		CookieGenerator cg = new CookieGenerator();
//			cg.setCookieName(VIEW_VOCABULARY.COOKIE_PERMANENT_LOGIN_USERNAME);
//			cg.setCookieMaxAge(0);
//			cg.setCookiePath("/GroupMe");
//			cg.removeCookie(response);
//			
//			cg = new CookieGenerator();
//			cg.setCookieName(VIEW_VOCABULARY.COOKIE_PERMANENT_LOGIN_CREDENTIAL);
//			cg.setCookieMaxAge(0);
//			cg.setCookiePath("/GroupMe");
//			cg.removeCookie(response);
			
//    		request.setAttribute("deleteCookies", "true");
//		}
    	
    	manager.logout();
    	mav.setViewName(VIEW_VOCABULARY.VIEW_WELCOME);

		return mav;
    }
    
    
    /**
     * Very small helper function to extract a given parameter or attribute from a HttpServletRequest.
     * @param request the {@link HttpServletRequest} object
     * @param parameter the parameter
     * @return the value of the parameter as a String
     */
    private String getStringValue(HttpServletRequest request, String parameter){
    	String value = request.getParameter(parameter);
    	if(value == null && request.getAttribute(parameter) != null){ //normally this is not accessed but we do our best to extract the value.. 
    		value = request.getAttribute(parameter).toString();
    	}
    	return value;
    }
}
